-----------------------------------------------------------------------------
-- 用于处理asr请求，收到请求后分发到asrclient中进行处理
-- 依赖：luasocket、cjson、penlight
-- Author: wchi
-----------------------------------------------------------------------------
local socket = require("socket")
local plformat = require("pl.pretty")
cjson = require "cjson"

host = host or "*"
port1 = port1 or 8080
port2 = port2 or 8181

if arg then
    host = arg[1] or host
    port1 = arg[2] or port1
    port2 = arg[3] or port2
end

--用于保存asrclient的连接信息
--其中，保存了asrclient的两种状态：free和busy
--free代表asr空闲，可以接受任务
--busy代表asr处于忙碌状态，不能接受任务
ASR_CLIENT_TB ={}

--用于保存client的asr请求信息
--其中，保存了请求的两种状态：todo、doing
--todo代表刚加入列表，还没分配asr
--doing代表请求已经发到asr，但还没收到回复
CLIENT_REQUEST_TB={}

--用于控制新建asrclient的速度，若是调用一次createNewClient，则将count设置到该变量中。
--以后在每次收到新的asrinit请求的时候，就将该变量减1，一直减到0，才能再次调用createNewClient。
GET_ASRCLIENT_LOCK=0;

function log(loglevel, content)
    if loglevel == "info" then
        print(content)
    end
end

--将asrClient保存的table中
function saveAsrClient(sclient, cid)
    log("debug","开始将asrClient存入本地ASR_CLIENT_TB中");
    local dt = {};
    dt["socket"] = sclient;
    dt["state"] = "free";
    dt["cid"] = cid;
    ASR_CLIENT_TB[cid] = dt;
end

--启动新的asrClient备用
function startNewAsrClient(count)
    log("debug","start to create new asrclient")
    local idprefix = os.time() .. "_" .. math.random(1,1000);
    for i=1, count do
        local cid = idprefix .. "_" .. i;
        os.execute("lua asrclient.lua " .. cid .. "&")
    end
end

--获取空闲的asrclient
function getFreeAsrClient()
    local count,total = 0,0;
    local freeAsr;
    for key, val in pairs(ASR_CLIENT_TB) do
        total = total + 1;
        if val then
            if val["state"] == "free" then
                count = count + 1;
                if not freeAsr then
                    freeAsr = val;
                end
            end
        end
    end

    log("debug",">>>>>>>>>>>>>>>CLIENT_REQUEST_TB")
    --log("debug",plformat.dump(CLIENT_REQUEST_TB))
    log("debug",">>>>>>>>>>>>>>>ASR_CLIENT_TB")
    --log("debug",plformat.dump(ASR_CLIENT_TB))

    log("info","current asr status: total:" .. total .. ", free:" .. count );

    --如果可用的asrClient数量小于5，则再次启动10个备用
    -- if count < 5 then
    --     log("debug","no enough free asrclient, start to create new ");
    --     startNewAsrClient(10);
    -- end

    return freeAsr;
end

--根据cid获取clientRequest
function getClientRequest(cid)
    local val = CLIENT_REQUEST_TB[cid];
    if not val then
        log("debug","not find client by cid:" .. (cid or ""))
    end

    return val;
end

--移除已经处理完的clientRequest
function removeClientRequest(cid)
    CLIENT_REQUEST_TB[cid] = nil;
end

function removeSocket(sclient)
    log("debug","开始移除socket:::")
    log("debug",sclient)
    for key, val in pairs(CLIENT_REQUEST_TB) do
        if val["socket"] == sclient then
            log("debug","开始从CLIENT_REQUEST_TB中移除客户请求， cid:" .. key);
            CLIENT_REQUEST_TB[key] = nil;
            break;
        end
    end

    for key, val in pairs(ASR_CLIENT_TB) do
        if val["socket"] == sclient then
            log("debug","开始从ASR_CLIENT_TB中移除asrClient请求， cid:" .. key);
            ASR_CLIENT_TB[key] = nil;
            break;
        end
    end
end

--设置clientRequest状态
function setClientRequestState(cid, state)
    local val = CLIENT_REQUEST_TB[cid];
    if val["state"] == state then
        log("debug","user request state:" .. state .. " not change.")
        return;
    end

    val["state"] = state;
    CLIENT_REQUEST_TB[cid] = val;
end

--设置asrClient状态
function setAsrClientState(cid, state)
    local val = ASR_CLIENT_TB[cid];
    if val then
        if val["state"] == state then
            log("debug","state:" .. state .. " not change.")
            return;
        end

        val["state"] = state;
        ASR_CLIENT_TB[cid] = val;
    else
        log("warn", "cid not exist in ASR_CLIENT_TB, cid:" .. cid);
    end
end

--保存client发来的asr请求信息
function saveClientRequst(sclient, request, jdata)
    local dt = {};
    dt["data"] = jdata["data"];
    dt["socket"] = sclient;
    dt["cid"] = jdata["cid"];
    dt["state"] = "todo";
    dt["initmsg"] = request;
    local cid = jdata["cid"];
    CLIENT_REQUEST_TB[cid] = dt;
end


-- simple set implementation
-- the select function doesn't care about what is passed to it as long as
-- it behaves like a table
-- creates a new set data structure
function newset()
    local reverse = {}
    local set = {}
    return setmetatable(set, {__index = {
        insert = function(set, value)
            if not reverse[value] then
                table.insert(set, value)
                reverse[value] = #set
            end
        end,
        remove = function(set, value)
            local index = reverse[value]
            if index then
                reverse[value] = nil
                local top = table.remove(set)
                if top ~= value then
                    reverse[top] = index
                    set[index] = top
                end
            end
        end
    }})
end

--将所有未处理的用户请求，进行派发
function dispatchRequest()
    local isAsrEnough = true;
    for key, val in pairs(CLIENT_REQUEST_TB) do
        if val then
            if val["state"] == "todo" then
                local aclint = getFreeAsrClient();
                if not aclint then
                    log("info","null asrClient")
                    isAsrEnough = false;
                    break;
                end 

                --获取到了asrclient资源
                local asrCid = aclint["cid"]
                log("info","获取到的free asrClient为" .. asrCid)

                if aclint["socket"] then
                    log("debug","将asr请求发送给" .. asrCid);
                    aclint["socket"]:send(val["initmsg"] .. "\n");

                    --将clientrequest的状态设置为doing，避免再次被分配asrclient
                    setClientRequestState(key, "doing");

                    --将asrClient状态设置为busy
                    setAsrClientState(asrCid,"busy");
                else
                    log("debug","请求到的通道存在异常，等下次继续处理， request cid:" .. key .. ", erro asr cid:" .. asrCid)
                end
            end
        end
    end

    if not isAsrEnough then
        if GET_ASRCLIENT_LOCK <= 0 then
            log("info","asr not enough ,start to create more");
            startNewAsrClient(10)
            GET_ASRCLIENT_LOCK = 10;
        else
            log("info","have started to create new asrClient， please wait...")
        end
    end
end

--处理接收到的消息
function handleMsg(sclient, msg)
    log("debug","接收到消息{" .. msg .. "}")   
    --判断接收到的消息类型
    -- local st, data =     pcall(cjson.decode, str)
    cjson = require "cjson"
    local status, data = pcall(cjson.decode, msg);
    if status and data then 
        sender = data["sender"] or "";
        msgtype = data["msgtype"] or "";
        mdata = data["data"] or "";
        cid = data["cid"] or "";

        if sender == "asrclient"then
            if msgtype == "init" then 
                handleAsrClientInit(sclient,cid);
            elseif msgtype == "asresponse" then
                handleAsrResponse(sclient, msg, data);
            end
        elseif sender == "client" then
            if msgtype == "asrequest" then
                handleClientRequest(sclient, msg, data);
            end
        else
            log("debug","未处理的消息:{" .. msg .. "}")
        end
    else
        log("debug","invalid msg type, not json:{" .. msg .. "}")
    end
end

--处理客户端发来的识别请求
function handleClientRequest(sclient, msg, jdata)
    log("debug","开始处理AsrRequest, msg:{" .. msg .. "}")
    saveClientRequst(sclient, msg, jdata);
end

--处理asrclient的识别结果消息
--找到相应的请求client，然后将结果转发过去
function handleAsrResponse(sclient, msg, jdata)
    log("debug","开始处理AsrResponse，msg:{" .. msg .. "}")
    local asrCid = jdata["cid"];
    local clientId = jdata["tocid"];
    local client = getClientRequest(clientId);
    if client then
        if client["socket"] then
            log("debug","send asr response to client:")
            client["socket"]:send(msg .. "\n");
        else
            log("debug","warn: null socket in client")
        end
    else
        log("debug","warn: no client by clientId:" .. clientId)
    end

    setAsrClientState(asrCid, "free");
    removeClientRequest(clientId)
end

--处理
function handleAsrClientInit(sclient, cid)
    log("info","收到新的AsrClient连接请求, cid:" .. cid .. ", GET_ASRCLIENT_LOCK:" .. GET_ASRCLIENT_LOCK);
    saveAsrClient(sclient, cid);

    if GET_ASRCLIENT_LOCK > 0 then
        GET_ASRCLIENT_LOCK = GET_ASRCLIENT_LOCK - 1;
    end
end

do
    server1 = assert(socket.bind(host, port1))
    server2 = assert(socket.bind(host, port2))
    server1:settimeout(1) -- make sure we don't block in accept
    server2:settimeout(1)

    log("debug","start 10 asr client...")
    startNewAsrClient(10)

    log("debug","Servers bound\n")

    set = newset()

    log("debug","Inserting servers in set\n")
    set:insert(server1)
    set:insert(server2)

    while 1 do
        local readable, _, error = socket.select(set, nil)
        --plformat.dump(readable);
        for _, input in ipairs(readable) do
            -- is it a server socket?
            if input == server1 or input == server2 then
                log("debug","Waiting for clients\n")
                local new = input:accept()
                if new then
                    new:settimeout(1)
                    log("debug","Inserting client in set\n")
                    set:insert(new)
                end
            -- it is a client socket
            else
                local line, error = input:receive()
                if error then
                    input:close()
                    log("debug","Removing client from set, error:{" .. error .. "}\n")
                    set:remove(input)
                    removeSocket(input)
                else
                    local status, msg = pcall(handleMsg, input, line)
                    log("debug","消息处理结果, status:{" .. tostring(status) .. "}, msg{" .. (msg or "") .. "}");
    			end
            end
        end

        local st, mg = pcall(dispatchRequest)
        if not st then
            log("debug","error in dispatchRequest:" .. (mg or ""))
        end
    end
end